﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using API.Models.Repository;
using System.Threading.Tasks;

namespace API.Controllers
{
    public class SecurityController : AsyncController
    {
        //
        // GET: /Security/

        private API.Models.SEC_SESSIONS GetSession(string api_key)
        {
            var sessionRepository = new SEC_SESSIONS_Repository();
            var ip = Request.RequestContext.HttpContext.Request.UserHostAddress;
            return sessionRepository.LogIn(ip, api_key);
        }


        public ActionResult Index()
        {
            return View();
        }

        public ActionResult Login(string email, string password, string api_key)
        {
            var sessionRepository = new SEC_SESSIONS_Repository();
            var session = new API.Models.SEC_SESSIONS();

            var ip = Request.RequestContext.HttpContext.Request.UserHostAddress;

            if (String.IsNullOrEmpty(api_key))
            {
                session = sessionRepository.LogIn(ip, email, password);
            }
            else
            {
                session = sessionRepository.LogIn(ip, api_key);
            }

            if (session == null)
                throw new Exception("Сессия не найдена");

            var api_session = new API.Models.API_SESSION
            {
                APIKEY = session.APIKEY,
                COMPANY_GUID = session.COMPANY_GUID,
                COMPANY_KEY = session.COMPANY != null ? session.COMPANY.EXT_KEY : String.Empty,
                COMPANYNAME = session.COMPANY != null ? session.COMPANY.COMPANY_NAME : "Компания не выбрана",
                EMAIL = session.USER.EMAIL,
                LOGON_DATETIME = session.LOGON_DATETIME,
                USER_GUID = session.USER_GUID,
                USERNAME = session.USER.USERNAME
            };

            return Json(api_session, JsonRequestBehavior.AllowGet);
        }

        public ActionResult ChangeCompany(string api_key, Guid company_key)
        {
            var sessionRepository = new SEC_SESSIONS_Repository();
            var companyRepository = new SEC_USER_COMPANIES_Repository();

            var session = sessionRepository.GetSession(api_key);
            companyRepository.SetSession(session);

            if (!companyRepository.Find().Any(r => r.ALLOW_TO_GUID == company_key && r.IS_ALLOWED))
                throw new Exception("Доступ к компании не предоставлен");

            session.COMPANY_GUID = company_key;

            sessionRepository.Edit(session);
            sessionRepository.Save();

            return Content("ОК");
        }

        public ActionResult AccessToUser(string email, Guid company_key)
        {
            var userRepository = new SEC_USERS_Repository();
            var userCompanyRepository = new SEC_USER_COMPANIES_Repository();
            var companyRepository = new WH_COMPANIES_Repository();

            var user = userRepository.Find(r => r.EMAIL == email).SingleOrDefault();
            var company = companyRepository.Find(r => r.GUID_RECORD == company_key).SingleOrDefault();

            if (company == null)
                throw new Exception("Компания не найдена");

            if (user == null)
            {
                var genPasswordLength = int.Parse(System.Configuration.ConfigurationManager.AppSettings["genPasswordLength"]);
                var password = API.Core.Util.PasswordGenerator(genPasswordLength, false);
                user = userRepository.Register(email, "NONAME", password);

                this.SendNewRegisterMail(email, "NONAME", password);
            }

            if (userCompanyRepository.Find(r =>
                r.USER_GUID == user.GUID_RECORD &&
                r.ALLOW_TO_GUID == company.GUID_RECORD &&
                r.IS_ALLOWED).Any())
            {
                throw new Exception(String.Format("Доступ пользователю {0} уже был предоставлен", email));
            }

            var access = new Models.SEC_USER_COMPANIES{
                ALLOW_TO_GUID = company.GUID_RECORD,
                GUID_RECORD = Guid.NewGuid(),
                IS_ALLOWED = true,
                SECURITY_CODE = "0000",
                USER_GUID = user.GUID_RECORD
            };

            userCompanyRepository.Add(access);
            userCompanyRepository.Save();

            this.SendNewAccessMail(email, company.COMPANY_NAME);

            return Content("ОК");
        }

        /// <summary>
        /// Для предоставления доступа к компании
        /// формируется секретный код и высылается оператору на электронный адрес
        /// </summary>
        /// <param name="api_key"></param>
        /// <returns></returns>
        public ActionResult AccessToCompany(string api_key)
        {
            var session = GetSession(api_key);
            if (session == null)
                throw new Exception("Сессия не найдена");

            var repository = new SEC_COMPANIES_ACCESS_REQUESTS_Repository();
            repository.SetSession(session);

            var password = API.Core.Util.PasswordGenerator(5, false);
            repository.ProvideAccess(password);

            this.SendProvideAccessMail(
                session.USER.EMAIL, session.COMPANY.COMPANY_NAME, password);

            return Content("ОК");
        }

        /// <summary>
        /// Для получения доступа к компании
        /// оператор указывает секретный код
        /// </summary>
        /// <param name="api_key"></param>
        /// <param name="code"></param>
        /// <returns></returns>
        public ActionResult AccessToCompanyUseKey(string api_key, string code)
        {
            var session = GetSession(api_key);
            if (session == null)
                throw new Exception("Сессия не найдена");

            var repository = new SEC_COMPANIES_ACCESS_REQUESTS_Repository();
            repository.SetSession(session);
            repository.UseAccess(code);

            //this.SendUseAccessMail(
            //    session.USER.EMAIL, access.COMPANY.COMPANY_NAME);

            return Content("ОК");
        }

        /// <summary>
        /// Активации доступа к компании
        /// </summary>
        /// <param name="api_key"></param>
        /// <param name="id"></param>
        /// <returns></returns>
        public ActionResult AccessToCompanyConfirm(string api_key, Guid id)
        {
            var session = GetSession(api_key);
            if (session == null)
                throw new Exception("Сессия не найдена");

            var repository = new SEC_COMPANIES_ACCESS_REQUESTS_Repository();
            repository.SetSession(session);
            repository.ConfirmAccess(id);

            var record = repository.Find(r => r.ACCESS_GUID.Value.Equals(id)).SingleOrDefault();

            this.SendConfirmAccessMail(
                session.USER.EMAIL, record.COMPANY.COMPANY_NAME);

            return Content("ОК");
        }


        private void SendProvideAccessMail(string email, string name, string password)
        {
            var message = @"
Уважаемый пользователь!

Для получения доступа к справочникам компании '{0}' необходимо активировать код '{1}'";

            var subject = "Предоставление доступа в EDEX";


            this.SendMail(email, subject, String.Format(message, name, password));

            //Task.Factory.StartNew(() =>
            //{
            //    this.SendAsyncMail(email, subject, String.Format(message, name, password));
            //});
        }

        private void SendUseAccessMail(string email, string name)
        {
            throw new NotImplementedException();
        }

        private void SendConfirmAccessMail(string email, string name)
        {
            var message = @"
Уважаемый пользователь!

Доступ к справочникам компании '{0}' предоставлен.";

            var subject = "Предоставление доступа в EDEX";

            this.SendMail(email, subject, String.Format(message, name));

            //Task.Factory.StartNew(() =>
            //{
            //    this.SendAsyncMail(email, subject, String.Format(message, name));
            //});
        }


        private void SendNewRegisterMail(string email, string name, string password)
        {
            var message = @"
Уважаемый пользователь {0}!

Ваш электронный адрес был указан при регистрации на портале системы электронного документооборота 'EDEX'.
Администратор системы должен проверить указанные регистрационные данные после чего активировать аккаунт.

Ваш email: {1}
Ваш пароль: {2}";

            var subject = "Регистрация в EDEX";

            this.SendMail(email, subject, String.Format(message, name, email, password));

            //Task.Factory.StartNew(() =>
            //{
            //    this.SendAsyncMail(email, subject, String.Format(message, name, email, password));
            //});
        }

        private void SendNewAccessMail(string email, string companyName)
        {
            var message = @"
Уважаемый пользователь!

Вам был предоставлен доступ администратора к компании '{0}'";

            var subject = "Предоставление доступа в EDEX";

            this.SendMail(email, subject, String.Format(message, companyName));

            //Task.Factory.StartNew(() =>
            //{
            //    this.SendAsyncMail(email, subject, String.Format(message, companyName));
            //});
        }
        
        [HttpPost]
        public async Task<ActionResult> Register(string email, string name)
        {
            var genPasswordLength = int.Parse(System.Configuration.ConfigurationManager.AppSettings["genPasswordLength"]);

            var repository = new SEC_USERS_Repository();
            var password = API.Core.Util.PasswordGenerator(genPasswordLength, false);
            repository.Register(email, name, password);

            this.SendNewRegisterMail(email, name, password);

            return await Task<ActionResult>.Factory.StartNew(() =>
            {
                return this.Login(email, password, String.Empty);
            });
        }

        
        //public async Task<ActionResult> Reset(string email)
        [HttpPost]
        public ActionResult Reset(string email)
        {
            var genPasswordLength = int.Parse(System.Configuration.ConfigurationManager.AppSettings["genPasswordLength"]);
            var SMTP_SERVER_HOST = System.Configuration.ConfigurationManager.AppSettings["SMTP_SERVER_HOST"];
            var SMTP_SERVER_PORT = int.Parse(System.Configuration.ConfigurationManager.AppSettings["SMTP_SERVER_PORT"]);
            var SMTP_EMAIL = System.Configuration.ConfigurationManager.AppSettings["EMAIL"];
            var SMTP_LOGIN = System.Configuration.ConfigurationManager.AppSettings["LOGIN"];
            var SMTP_PASSWORD = System.Configuration.ConfigurationManager.AppSettings["PASSWORD"];
            var SMTP_USE_SSL = Convert.ToBoolean(System.Configuration.ConfigurationManager.AppSettings["SMTP_USE_SSL"]);

            var repository = new SEC_USERS_Repository();
            var password = API.Core.Util.PasswordGenerator(genPasswordLength, false);
            repository.ResetPassword(email, password);

            var message = @"
Уважаемый пользователь!
Ваш пароль на портале системы электронного документооборота 'EDEX' был изменен.

Ваш email: {0}
Ваш пароль: {1}";

            var subject = "Изменение учетных данных в EDEX";

            var mail = new API.Core.Mail(SMTP_SERVER_HOST, SMTP_SERVER_PORT, SMTP_LOGIN, SMTP_PASSWORD, SMTP_USE_SSL);
            mail.Send(new Core.Mail.MailItem
            {
                From = SMTP_EMAIL,
                To = email,
                Subject = subject,
                Body = String.Format(message, email, password)
            });

            return Content("OK");

            /*
            var mail = new API.Core.AsyncMail(SMTP_SERVER_HOST, SMTP_SERVER_PORT, SMTP_LOGIN, SMTP_PASSWORD);
            mail.Send(new Core.AsyncMail.MailItem
            {
                From = SMTP_EMAIL,
                To = email,
                Subject = subject,
                Body = String.Format(message, email, password)
            });

            return await Task<ActionResult>.Factory.StartNew(() =>
            {
                return Content("OK");
            });
            */
        }



        /// <summary>
        /// Отправка электронного сообщения
        /// </summary>
        /// <param name="email"></param>
        /// <param name="subject"></param>
        /// <param name="message"></param>
        private async void SendAsyncMail(string email, string subject, string message)
        {
            var SMTP_SERVER_HOST = System.Configuration.ConfigurationManager.AppSettings["SMTP_SERVER_HOST"];
            var SMTP_SERVER_PORT = int.Parse(System.Configuration.ConfigurationManager.AppSettings["SMTP_SERVER_PORT"]);
            var SMTP_EMAIL = System.Configuration.ConfigurationManager.AppSettings["EMAIL"];
            var SMTP_LOGIN = System.Configuration.ConfigurationManager.AppSettings["LOGIN"];
            var SMTP_PASSWORD = System.Configuration.ConfigurationManager.AppSettings["PASSWORD"];

            var mail = new API.Core.AsyncMail(SMTP_SERVER_HOST, SMTP_SERVER_PORT, SMTP_LOGIN, SMTP_PASSWORD);

            await System.Threading.Tasks.Task.Factory.StartNew(() =>
            {
                mail.Send(new Core.AsyncMail.MailItem
                {
                    From = SMTP_EMAIL,
                    To = email,
                    Subject = subject,
                    Body = message
                });
            });
        }

        private void SendMail(string email, string subject, string message)
        {
            var SMTP_SERVER_HOST = System.Configuration.ConfigurationManager.AppSettings["SMTP_SERVER_HOST"];
            var SMTP_SERVER_PORT = int.Parse(System.Configuration.ConfigurationManager.AppSettings["SMTP_SERVER_PORT"]);
            var SMTP_EMAIL = System.Configuration.ConfigurationManager.AppSettings["EMAIL"];
            var SMTP_LOGIN = System.Configuration.ConfigurationManager.AppSettings["LOGIN"];
            var SMTP_PASSWORD = System.Configuration.ConfigurationManager.AppSettings["PASSWORD"];
            var SMTP_USE_SSL = Convert.ToBoolean(System.Configuration.ConfigurationManager.AppSettings["SMTP_USE_SSL"]);

            var mail = new API.Core.Mail(SMTP_SERVER_HOST, SMTP_SERVER_PORT, SMTP_LOGIN, SMTP_PASSWORD, SMTP_USE_SSL);

            mail.Send(new Core.Mail.MailItem
            {
                From = SMTP_EMAIL,
                To = email,
                Subject = subject,
                Body = message
            });
        }
    }
}
